package org.moonlight.jvm.rtda.share.methodarea;

import org.moonlight.jvm.rtda.share.heap.ClassLoader;
import org.moonlight.jvm.rtda.share.Object;

import java.util.HashMap;
import java.util.Map;

/**
 * 字符串常量池
 *
 * 在class文件中，字符串是以 MUTF8格式 保存的。在Java虚拟机运行期间，
 * 字符串以java.lang.String（后面简称String）对象的形式存在，而在String对象内部，
 * 字符串又是以UTF16格式保存的。字符串相关功能大部分都是由String （和StringBuilder等）类提供的。
 * String类有两个实例变量。其中一个是value，类型是字符数组，用于存放UTF16编码后的字符序列。另一个是hash，缓存计字符串的哈希码。
 *
 * @author Moonlight
 * @createTime 2023/9/20 10:34
 **/
public class StringPool {

    /** 以 java 的 String 作为 key，自己写的 Object 作为 value 组成一个 Map **/
    private static final Map<String, Object> INTERNED_STR = new HashMap<>();

    /**
     * 将 原始的 Java String 字符串 转为 自定义的 Object 对象
     *
     * @param loader 类加载器
     * @param rawJavaString 原始的 Java String 字符串
     * @return Object {@link org.moonlight.jvm.rtda.share.Object} 自定义 Object 对象
     * @createTime 11:36 2023/9/20
     * @author moonlight
     **/
    public static Object myString(ClassLoader loader, String rawJavaString) {
        Object obj = INTERNED_STR.get(rawJavaString);
        if (obj != null) {
            return obj;
        }
        // java  的 String 转为 char 数组
        char[] chars = rawJavaString.toCharArray();
        // 再将 char 数组 转为 自定义的 Object
        Object jChars = new Object(loader.loadClass("[C"), chars);
        // 构建一个 java String 类型 的 自定义 Object
        Object myString = loader.loadClass("java/lang/String").newObject();
        // 给 自定义 Object 设置值
        myString.setRefVal("value", "[C", jChars);

        INTERNED_STR.put(rawJavaString, myString);
        return myString;
    }

    /**
     * 将 自定义 Object 对象 转为 Java String 对象
     *
     * @param myString {@link Object} 自定义 Object 对象
     * @return String Java String 字符串对象
     * @createTime 11:42 2023/9/20
     * @author moonlight
     **/
    public static String javaString(Object myString) {
       Object charArr = myString.getRefVal("value", "[C");
       return new String(charArr.chars());
    }

    /**
     *  如果字符串还没有入池，把它放入并返回该字符串，否则找到已入池字符串并返回
     *
     * @param myString {@link Object} 自定义 Object 对象
     * @return {@link Object} 自定义 Object 对象
     * @createTime 16:21 2023/9/22
     * @author moonlight
     **/
    public static Object internString(Object myString) {
        String string = javaString(myString);
        if (INTERNED_STR.containsKey(string)) {
            return INTERNED_STR.get(string);
        }
        INTERNED_STR.put(string, myString);
        return myString;
    }
}
